package com.shanhai.wallpapers.network.remote

import com.shanhai.wallpapers.fragments.download.DownloadResult
import com.shanhai.wallpapers.network.api.PexelsApi
import com.shanhai.wallpapers.network.request.ApiRequest
import com.shanhai.wallpapers.network.response.WallpapersResponse
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import java.io.File
import java.io.InputStream
import java.io.OutputStream

class PexelsRemoteDataSource(private val pexelsApi: PexelsApi) : ApiRequest() {
    suspend fun getWallpapers(query: String, page: Int): WallpapersResponse {
        return apiRequest { pexelsApi.getWallPapers(query, page) }
    }

    fun downloadWallpaper(url: String, directory: File): Flow<DownloadResult> {
        return flow {
            try {
                emit(DownloadResult.Progress(0))
                if (directory.exists().not()) {
                    directory.mkdir()
                }
                val fileName = url.substringAfterLast("/")
                    .substringBefore("?")
                    .substringBefore("#")
                val file = File(directory, fileName)
                val downloadFileName = pexelsApi.downloadWallpaper(url)
                with(downloadFileName) {
                    byteStream().use { inputStream ->
                        file.outputStream().use { outputStream ->
                            val totalBytes = contentLength()
                            val data = ByteArray(8_192)
                            var progress = 0L
                            while (true) {
                                val bytes = inputStream.readValue(data)
                                if (bytes == -1) {
                                    break
                                }
                                outputStream.channel
                                outputStream.writeValue(data, 0, bytes)
                                progress += bytes
                                emit(
                                    DownloadResult.Progress(
                                        ((progress * 100) / totalBytes).toInt()
                                    )
                                )
                            }
                        }
                    }
                }
                emit(DownloadResult.Completed(file))
            } catch (exception: Exception) {
                emit(DownloadResult.Error(exception.message.toString()))
            }
        }.flowOn(Dispatchers.IO).distinctUntilChanged()
    }

    private fun InputStream.readValue(data: ByteArray): Int {
        return read(data)
    }

    private fun OutputStream.writeValue(data: ByteArray, off: Int, length: Int) {
        return write(data, off, length)
    }
}